package com.aurora.leetcode.multithreading;

import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author : qiaodan
 * @date : 2024/9/5 17:42
 * @description 1117. H2O 生成
 * <p>
 * 现在有两种线程，氧 oxygen 和氢 hydrogen，你的目标是组织这两种线程来产生水分子。
 * 存在一个屏障（barrier）使得每个线程必须等候直到一个完整水分子能够被产生出来。
 * 氢和氧线程会被分别给予 releaseHydrogen 和 releaseOxygen 方法来允许它们突破屏障。
 * 这些线程应该三三成组突破屏障并能立即组合产生一个水分子。
 * 你必须保证产生一个水分子所需线程的结合必须发生在下一个水分子产生之前。
 * 换句话说:
 * 如果一个氧线程到达屏障时没有氢线程到达，它必须等候直到两个氢线程到达。
 * 如果一个氢线程到达屏障时没有其它线程到达，它必须等候直到一个氧线程和另一个氢线程到达。
 * 书写满足这些限制条件的氢、氧线程同步代码。
 * </p>
 * <p>
 * 示例 1:
 * </p>
 * <p>
 * 输入: water = "HOH"
 * 输出: "HHO"
 * 解释: "HOH" 和 "OHH" 依然都是有效解。
 * </p>
 * <p>
 * 示例 2:
 * </p>
 * <p>
 * 输入: water = "OOHHHH"
 * 输出: "HHOHHO"
 * 解释: "HOHHHO", "OHHHHO", "HHOHOH", "HOHHOH", "OHHHOH", "HHOOHH", "HOHOHH" 和 "OHHOHH" 依然都是有效解。
 * </p>
 */
public class H2O_1117 {
    public H2O_1117() {

    }

    AtomicInteger o = new AtomicInteger(0);
    AtomicInteger h = new AtomicInteger(0);
    ReentrantLock lock = new ReentrantLock(true);
    Condition oWait = lock.newCondition();
    Condition hWait = lock.newCondition();
    ArrayBlockingQueue<Runnable> oWaitArr = new ArrayBlockingQueue<>(20);
    ArrayBlockingQueue<Runnable> hWaitArr = new ArrayBlockingQueue<>(40);


    /**
     * 我的版本：阻塞队列
     */
    public void hydrogen1(Runnable releaseHydrogen){
        synchronized (this){
            hWaitArr.add(releaseHydrogen);
            if(oWaitArr.size() >= 1 && hWaitArr.size() >=2){
                    oWaitArr.remove().run();
                    hWaitArr.remove().run();
                    hWaitArr.remove().run();
            }
        }

    }
    public void oxygen1(Runnable releaseOxygen){
        synchronized (this){
            oWaitArr.add(releaseOxygen);
            if(oWaitArr.size() >= 1 && hWaitArr.size() >=2){
                oWaitArr.remove().run();
                hWaitArr.remove().run();
                hWaitArr.remove().run();
            }
        }
    }

    /**
     * 参考实现：信号量法
     */
    Semaphore oSem = new Semaphore(0);
    Semaphore hSem = new Semaphore(2);
    public void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
        hSem.acquire();
        releaseHydrogen.run();
        oSem.release();
    }
    public void oxygen(Runnable releaseOxygen) throws InterruptedException {
        oSem.acquire(2);
        releaseOxygen.run();
        hSem.release(2);
    }



    public static void main(String[] args) throws InterruptedException {
        H2O_1117 obj = new H2O_1117();
        Scanner scan = new Scanner(System.in);
        while(scan.hasNext()){
            String str = scan.next();
            for(int i=0; i<str.length(); ++i){
                switch (str.charAt(i)){
                    case 'O':
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    obj.oxygen(new Runnable() {
                                        @Override
                                        public void run() {
                                            System.out.print("O");
                                        }
                                    });
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        }).start();
                        break;
                    case 'H':
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    obj.hydrogen(new Runnable() {
                                        @Override
                                        public void run() {
                                            System.out.print("H");
                                        }
                                    });
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        }).start();
                }
            }
        }
        scan.close();
    }



}
